
#include "unpifi.h"

#include <sys/ioctl.h>
#include <sys/types.h>
#include <inttypes.h>
#include <netinet/in.h>

struct ifi_info * get_ifi_info(int family, int doaliaes) {

    struct ifi_info * ifi, * ifihead, ** ifipnext;
    int sockfd, len, lastlen, flags, myflags, idx = 0, hlen = 0;
    char * ptr, * buf, lastname[IF_NAMESIZE], * cptr, * haddr, *sdlname;

    struct ifconf ifc;
    struct ifreq  *ifr, ifrcopy;
    struct sockaddr_in  * sinptr;
    struct sockaddr_in6 * sin6ptr;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    lastlen = 0;
    len = 100 * sizeof(struct ifreq);   /*initial buffer size guess*/
    for (; ; ) {
        // get memory
        buf = malloc(len);
        ifc.ifc_len = len;
        ifc.ifc_ifcu.ifcu_buf = buf;

        // SIOCGIFCONF获取所有的接口列表
        if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {

            // siocg ifconf
            if(errno != EINVAL || lastlen != 0) {

                printf("ioctl error\n");
                break;
            }
        } else {

            if(ifc.ifc_len == lastlen) {

                break;
            }
        }
        // 得到一个以sizeof(struct ifreq)以长度为基础的值
        lastlen = lastlen + 10 * sizeof(struct ifreq);
        free(buf);
    }
    ifihead = NULL;
    ifipnext = &ifihead;
    lastname[0] = 0;
    sdlname = NULL;

    for (ptr = buf; ptr < buf + ifc.ifc_len; ) {

        ifr = (struct ifreq*)ptr;
#ifdef HAVE_SOCKADDR_SA_LEN

        len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
#else
        switch(ifr->ifr_ifru.ifru_addr.sa_family)
        {
#ifdef  IPV6
        case AF_INET6:
            len = sizeof(struct sockaddr_in6);
            break;
#endif
        case AF_INET:
        default:
            len = sizeof(struct sockaddr);
            break;
        }
#endif /*HAVE_SOCKADDR_SA_LEN*/

        // 指向下一个结构（即下一个struct freq值）
        ptr += sizeof(ifr->ifr_name) + len;    /*for next one in buffer*/

#ifdef  HAVE_SOCKADDR_DL_STRUCT
        /*assume that AF_LINK precedes AF_INET or AF_INET6*/
        if (ifr->ifr_ifru.ifru_addr.sa_family == AF_NETLINK) {

            struct sockaddr_dl * sdl = (struct sockaddr_dl*)&ifr->ifr_addr;
            sdlname = ifr->ifr_name;
            idx = sdl->sdl_index;

            haddr = sdl->sdl_data + sdl->sdl_nlen;
            hlen = sdl->sdl_alen;
        }
#endif
        // 对于不是指定的FAMILY值 则跳过（不符合预期的值）
        if (ifr->ifr_ifru.ifru_addr.sa_family != family) {

            continue;
        }
        myflags = 0;
        if (cptr = strchr(ifr->ifr_ifrn.ifrn_name,':') != NULL) {

            *cptr = 0;
        }
        if (strncmp(lastname, ifr->ifr_ifrn.ifrn_name, IF_NAMESIZE) == 0) {

            if (doaliaes == 0) {

                continue;
            }
            myflags = IFI_ALLAS;
        }
        memcpy(lastname, ifr->ifr_ifrn.ifrn_name, IF_NAMESIZE);

        ifrcopy = *ifr;
        /*获取接口标志*/
        ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
        flags = ifrcopy.ifr_ifru.ifru_flags;

        if ((flags & IFF_UP) == 0) {

            continue;   /*ignore if interface not up*/
        }
        // 分配一个内存（结构体为 struct ifi_info）
        ifi = calloc(1, sizeof(struct ifi_info));
        *ifipnext = ifi;
        ifipnext = &ifi->ifi_next;
        
        ifi->ifi_flags = flags;         /*IFF_XXX values*/
        ifi->ifi_myflags = myflags;     /*IFI_XXX values*/
#if defined(SIOCGIFMTU) && defined(HAVE_STRUCT_IFREQ_MTU)
        // 获取mtu的值
        ioctl(sockfd, SIOCGIFMTU, &ifrcopy);
        ifi->ifi_mtu = ifrcopy.ifr_mtu;
#else
        ifi->ifi_mtu = 0;
#endif
        memcpy(ifi->ifi_name, ifr->ifr_ifrn.ifrn_name, IFI_NAME);

        ifi->ifi_name[IFI_NAME -1] = '\0';
        /* if the sockaddr dl is from a different interface ignore it*/
        if (sdlname == NULL || strcmp(sdlname, ifr->ifr_ifrn.ifrn_name) != 0) {

            idx = hlen = 0;
        }
        ifi->ifi_index = 0;
        ifi->ifi_hlen  = hlen;

        if (ifi->ifi_hlen > IFI_HADDR) {
            
            // 限制数值
            ifi->ifi_hlen = IFI_HADDR;
        }
        if (hlen) {

            memcpy(ifi->ifi_haddr, haddr, ifi->ifi_hlen);
        }
        // 下面是对IPV4 和 IPV6不同协议做一样的操作 
        switch(ifr->ifr_ifru.ifru_addr.sa_family) 
        {
        case AF_INET:
            // 获取到 struct sockaddr 
            sinptr = (struct sockaddr_in *)&ifr->ifr_ifru.ifru_addr;
            // 分配内存
            ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in));
            // 拷贝内存
            memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
#ifdef  SIOCGIFBRDADDR  /*广播地址*/
            if (flags & IFF_BROADCAST) {

                ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
                sinptr = (struct sockaddr_in *)&ifrcopy.ifr_ifru.ifru_broadaddr;
                ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in));
                memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
            }
#endif 

#ifdef SIOCGIFDSTADDR /*目的地地址*/
            // point to point (点对点)
            if (flags & IFF_POINTOPOINT) {

                ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
                sinptr = (struct sockaddr_in *)&ifr->ifr_ifru.ifru_dstaddr;
                ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in));
                memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
            }
#endif
            break;
        case AF_INET6:
            sin6ptr = (struct sockaddr_in6 * )&ifr->ifr_ifru.ifru_addr;

            ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6));
            memcpy(ifi->ifi_addr, sin6ptr, sizeof(struct sockaddr_in6));

#ifdef SIOCGIFDSTADDR
            if (flags & IFF_POINTOPOINT) {

                ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
                sin6ptr = (struct sockaddr_in6*)&ifrcopy.ifr_ifru.ifru_dstaddr;
                ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in6));
                
                memcpy(ifi->ifi_dstaddr,sin6ptr, sizeof(struct sockaddr_in6));
            }
#endif
            break;
        default:
            break;
        }
    }
    free(buf);
    return(ifihead);
}

void free_ifi_info(struct ifi_info * ifihead) {

    struct ifi_info * ifi, * ifinext;
    // 释放链表内存以及成员变量内存
    for (ifi = ifihead; ifi != NULL; ifi = ifinext) {

        if (ifi->ifi_addr != NULL) {

            free(ifi->ifi_addr);
        }
        if (ifi->ifi_dstaddr != NULL) {

            free(ifi->ifi_dstaddr);
        }
        if (ifi->ifi_brdaddr != NULL) {

            free(ifi->ifi_brdaddr);
        }
        ifinext = ifi->ifi_next;
        free(ifi);
    }
}